/**
 * @Copyright ®2015 Sinosoft Co. Ltd. All rights reserved.<br/>
 * 项目名称 : 中科软支付平台
 * 创建日期 : 2016年12月27日
 * 修改历史 : 
 *     1. [2016年12月27日]创建文件 by liuyujia
 */
package com.sinosoft.sinopay.web.weixinpay.web.controller;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;

import com.sinosoft.sinocloud.platform.base.SinoBaseController;
import com.sinosoft.sinocloud.platform.common.util.SecurityUtil;
import com.sinosoft.sinocloud.platform.common.util.XStreamComponent;
import com.sinosoft.sinopay.api.common.dto.pay.PaymentInfoDetailDTO;
import com.sinosoft.sinopay.paytrade.config.ENUM_PAY_STATUS;
import com.sinosoft.sinopay.paytrade.dto.PayTradeNotifyRecordDTO;
import com.sinosoft.sinopay.paytrade.dto.PaymentTradeReqDTO;
import com.sinosoft.sinopay.paytrade.facade.PaymentTradeQueryFacadeService;
import com.sinosoft.sinopay.web.weixinpay.biz.config.ENUM_REPORT_NOTIFYTYPE;
import com.sinosoft.sinopay.web.weixinpay.biz.config.WechatConfig;
import com.sinosoft.sinopay.web.weixinpay.biz.service.PaymentWechatService;
import com.sinosoft.sinopay.web.weixinpay.dto.WechatNotifyRequestDTO;

/**  
 * 【微信支付后台回调入口Controller】
 * @author liuyujia
 * @date 2016年12月27日 下午3:08:46 
 * @version V1.0  
 */
@Controller
@RequestMapping("/sinopay/wechatNotify/")
public class WechatPayNotifyController extends SinoBaseController{
	
	@Autowired
	private PaymentWechatService paymentWechatService;
	@Autowired
	private PaymentTradeQueryFacadeService paymentTradeQueryFacadeService;
	@Autowired
	private WechatConfig wechatConfig;
	
	@RequestMapping(value = "wechatNotify.do")
	public String wechatNotify(HttpServletRequest request, HttpServletResponse response, ModelMap model) throws IOException {
		logger.info("接收微信后台回调通知......");
		String resBackWx = null;
		try{
			//接收参数
			InputStream inputStream = request.getInputStream();
			String xml = readInput(inputStream);
			//解析回调信息
			XStreamComponent xtream = XStreamComponent.newInstance();
			xtream.processAnnotations(WechatNotifyRequestDTO.class);
			WechatNotifyRequestDTO tWechatNotifyRequestDTO =(WechatNotifyRequestDTO) xtream.fromXML(xml);
			logger.info("微信支付通知结果:"+tWechatNotifyRequestDTO.toString());
			//通信成功返回，返回微信成功
			logger.info("微信支付通信结果:"+tWechatNotifyRequestDTO.getReturn_code());
			if (tWechatNotifyRequestDTO.getReturn_code().equals("SUCCESS")){
				// 判断签名及结果
			    if(getSignVeryfy(tWechatNotifyRequestDTO,tWechatNotifyRequestDTO.getSign())){
			    	logger.info("签名正确");
					String outTradeNo = tWechatNotifyRequestDTO.getOut_trade_no();
					logger.info("outTradeNo="+outTradeNo);
					PaymentInfoDetailDTO tPaymentInfoDetailDTO = paymentWechatService.getPaymentTradeForPayNo(outTradeNo);
					if(tPaymentInfoDetailDTO==null){
						logger.info("订单查询失败");
						throw new Exception("订单查询失败");
					}
					logger.info("查询回调记录表开始......");
					List<PayTradeNotifyRecordDTO> lPayTradeNotifyRecordDTO = paymentWechatService.queryPayTradeNotifyRecord(outTradeNo);
					logger.info("订单号："+outTradeNo+"回调表记录:"+lPayTradeNotifyRecordDTO.size());
					if(lPayTradeNotifyRecordDTO!=null && lPayTradeNotifyRecordDTO.size()>0){
						logger.info("防止微信多次回调影响业务处理预判responsecode");
						Map<String, Object> resBcakWXMap = new HashMap<String, Object>();
						resBcakWXMap.put("return_code", "SUCCESS");
						resBcakWXMap.put("return_msg", "OK");
						resBackWx = resBcakWXMap.toString(); //TODO 返回报文，记录日志用
						sendToWx(response,"SUCCESS","OK");
					}else{
						logger.info("更新支付主表");
						//更新支付主表
						String resultCode = tWechatNotifyRequestDTO.getResult_code();
						String transCode = null;
						String payStatus = null;
						PaymentTradeReqDTO tPaymentTradeReqDTO = new PaymentTradeReqDTO();
						if(resultCode.equals("SUCCESS")){
							transCode = resultCode;
							payStatus = ENUM_PAY_STATUS.PAY_PAYED.code();
							logger.info("开始更新交易订单表");
							paymentWechatService.modifyTradeOrderStatus(outTradeNo, payStatus);
						}
						else{
							transCode = tWechatNotifyRequestDTO.getErr_code();
							payStatus = ENUM_PAY_STATUS.PAY_FAIL.code();
							logger.info("开始更新交易订单表");
							paymentWechatService.modifyTradeOrderStatus(outTradeNo, payStatus);
						}
						logger.info("开始更新交易主表");
						tPaymentTradeReqDTO.setPayStatus(payStatus);
						tPaymentTradeReqDTO.setPayNo(outTradeNo);//支付号码
						tPaymentTradeReqDTO.setReturnCode(transCode);//第三方返回状态码
						tPaymentTradeReqDTO.setReturnMsg(tWechatNotifyRequestDTO.getErr_code_des());//第三方返回信息
						tPaymentTradeReqDTO.setPayTime(tWechatNotifyRequestDTO.getTime_end());//支付成功时间
						tPaymentTradeReqDTO.setPayDate(new Date());//支付日期
						tPaymentTradeReqDTO.setThirdPayNo(tWechatNotifyRequestDTO.getTransaction_id());//第三方支付号码
						tPaymentTradeReqDTO.setModifyDate(new Date());
						paymentWechatService.modifyPaymentInfo(tPaymentTradeReqDTO);
																						
						//存回调机制表
						logger.info("开始存储回调机制表");
						PayTradeNotifyRecordDTO cPayTradeNotifyRecordDTO = new PayTradeNotifyRecordDTO();
						cPayTradeNotifyRecordDTO.setMerchantCode(tPaymentInfoDetailDTO.getMerchantCode());
						cPayTradeNotifyRecordDTO.setPayNo(outTradeNo);
						String notifyUrl = paymentWechatService.getNotifyUrlByMerchant(tPaymentInfoDetailDTO.getMerchantCode(), ENUM_REPORT_NOTIFYTYPE.BACK_NOTIFY.code());
						cPayTradeNotifyRecordDTO.setNotifyUrl(notifyUrl);
						cPayTradeNotifyRecordDTO.setNotifyType(ENUM_REPORT_NOTIFYTYPE.BACK_NOTIFY.code());
						cPayTradeNotifyRecordDTO.setNotifyTimes(0);
						cPayTradeNotifyRecordDTO.setLimitNotifyTimes(3);
						cPayTradeNotifyRecordDTO.setCreateDate(new Date());
						paymentWechatService.addPayTradeNotifyRecord(cPayTradeNotifyRecordDTO);
						sendToWx(response,"SUCCESS","OK");
					}
			    }else{
			    	logger.error("后台微信支付通知验签error:signerror");
			    }
			}else{
				logger.error("后台微信支付通知通讯error:"+ tWechatNotifyRequestDTO.getReturn_msg());
			}
		}
		catch(Exception e){
			logger.info("微信回调接口异常:" + e.getMessage()+"错误信息："+e.getStackTrace());
		}
		return null;
	}

	public void sendToWx(HttpServletResponse response,String return_code,String return_msg ) throws IOException{
		String strHtml = String.format("<xml><return_code>%s</return_code><return_msg>%s</return_msg></xml>",
				return_code,return_msg);
		try{
		PrintWriter out = response.getWriter();
		out.println(strHtml);
		out.flush();
		out.close();
		}catch(IOException io){
			io.printStackTrace();
		}
	}

	/**
	 * 读取inputstream内容
	 * @param inputStream
	 * @return
	 * @throws IOException
	 */
	private String readInput(InputStream inputStream) throws IOException {
		if (inputStream == null)
			return "传输数据为空";
		StringBuffer sb = new StringBuffer();
		BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
		String line = null;
		while ((line = reader.readLine()) != null) {
			sb.append(line);
		}

		return sb.toString();
	}

	/**
	 * 验证签名
	 * @param tWechatNotifyRequestDTO
	 * @param sign
	 * @return
	 * @throws UnsupportedEncodingException
	 */
	public boolean getSignVeryfy(WechatNotifyRequestDTO tWechatNotifyRequestDTO,String sign)throws UnsupportedEncodingException {
    	String coupon_fee =null;
    	String coupon_count =null;
    	if(tWechatNotifyRequestDTO.getCoupon_fee()!=0){
    		coupon_fee =String.valueOf(tWechatNotifyRequestDTO.getCoupon_fee());
    		logger.info("使用代金券或立减优惠金额："+coupon_fee);
    	}
    	if(tWechatNotifyRequestDTO.getCoupon_count()!=0){
    		coupon_count = String.valueOf(tWechatNotifyRequestDTO.getCoupon_count());
    		logger.info("使用代金券或立减优惠使用数量："+coupon_count);
    	}
    	Map<String,String> params = new TreeMap<String,String>();
    	params.put("return_code", tWechatNotifyRequestDTO.getReturn_code());
    	params.put("return_msg",tWechatNotifyRequestDTO.getReturn_msg());
    	params.put("appid", tWechatNotifyRequestDTO.getAppid());
    	params.put("mch_id", tWechatNotifyRequestDTO.getMch_id());
    	params.put("device_info", tWechatNotifyRequestDTO.getDevice_info());
    	params.put("nonce_str", tWechatNotifyRequestDTO.getNonce_str());
    	params.put("result_code", tWechatNotifyRequestDTO.getResult_code());
    	params.put("err_code", tWechatNotifyRequestDTO.getErr_code());
    	params.put("err_code_des", tWechatNotifyRequestDTO.getErr_code_des());
    	params.put("openid", tWechatNotifyRequestDTO.getOpenid());
    	params.put("is_subscribe", tWechatNotifyRequestDTO.getIs_subscribe());
    	params.put("trade_type", tWechatNotifyRequestDTO.getTrade_type());
    	params.put("bank_type", tWechatNotifyRequestDTO.getBank_type());
    	params.put("total_fee", String.valueOf(tWechatNotifyRequestDTO.getTotal_fee()));
    	params.put("fee_type", tWechatNotifyRequestDTO.getFee_type());
    	params.put("cash_fee", String.valueOf(tWechatNotifyRequestDTO.getCash_fee()));
    	params.put("cash_fee_type", tWechatNotifyRequestDTO.getCash_fee_type());
    	params.put("coupon_fee", coupon_fee);
    	params.put("coupon_count", coupon_count);
    	params.put("coupon_id_$n",tWechatNotifyRequestDTO.getCoupon_id_$n());
    	params.put("coupon_fee_$n",tWechatNotifyRequestDTO.getCoupon_fee_$n());
    	params.put("transaction_id",tWechatNotifyRequestDTO.getTransaction_id());
    	params.put("out_trade_no",tWechatNotifyRequestDTO.getOut_trade_no());
    	params.put("attach",tWechatNotifyRequestDTO.getAttach());
    	params.put("time_end",tWechatNotifyRequestDTO.getTime_end());
		// 过滤空值、sign与sign_type参数
		// 获得签名验证结果
		String preSignStr = wechatSign(params);
		logger.info("7.支付平台加密信息："+preSignStr);
		logger.info("8.微信加密信息："+sign);
		boolean isSign = preSignStr.equals(sign);
		return isSign;
	}

	public String wechatSign(Map<String, String> params) throws UnsupportedEncodingException{
        Map<String, String> sParaNew = paraFilter(params);
        String prestr = createLinkString(sParaNew);
        logger.info("4.获取待签名拼接字符串："+prestr);
        String signStr = prestr + "&key=" + wechatConfig.getWechatKey();
        logger.info("5.获取待签名拼接字符串+key："+signStr);
        String sign = SecurityUtil.Md5(signStr).toUpperCase();
        logger.info("6.加密后为"+sign);
        return sign;
    }

	/** 
     * 把数组所有元素排序，并按照“参数=参数值”的模式用“&”字符拼接成字符串
     * @param params 需要排序并参与字符拼接的参数组
     * @return 拼接后字符串
     */
    public String createLinkString(Map<String, String> params) {
        List<String> keys = new ArrayList<String>(params.keySet());
        Collections.sort(keys);
        String prestr = "";
        for (int i = 0; i < keys.size(); i++) {
            String key = keys.get(i);
            String value = params.get(key);

            if (i == keys.size() - 1) {//拼接时，不包括最后一个&字符
                prestr = prestr + key + "=" + value;
            } else {
                prestr = prestr + key + "=" + value + "&";
            }
        }
        return prestr;
    }

	/** 
     * 除去数组中的空值和签名参数
     * @param sArray 签名参数组
     * @return 去掉空值与签名参数后的新签名参数组
     */
    public Map<String, String> paraFilter(Map<String, String> sArray) {

        Map<String, String> result = new TreeMap<String, String>();

        if (sArray == null || sArray.size() <= 0) {
            return result;
        }

        for (String key : sArray.keySet()) {
            String value = sArray.get(key);
            if (value == null || value.equals("")) {
                continue;
            }
            result.put(key, value);
        }
        logger.info("3.待签名字段："+result.toString());
        return result;
    }
	
}
